#include "JsonSerializer.h"

#include "Framework/Application.h"
#include "Object/FindObject.h"
#include "Reflection/Enum.h"
#include "Reflection/ObjectPtr.h"
#include "Reflection/MapType.h"
#include "Reflection/SubclassOfType.h"
#include "Containers/ReflectableVector.h"
#include "Containers/ReflectableMap.h"
#include "Output/Console.h"

#include "StringSerializer.h"

std::shared_ptr<CJsonNodeBase> CJsonSerializer::SerializeToJson(const void *InFieldPtr, CProperty *InProperty)
{
     auto Type=InProperty->GetType().BasicType;

     bool IsBinaryValue=InProperty->HasFlag(EPropertyFlags::P_SerializeAsBinary);

     if(InProperty->IsField())
     {   
          const void* ValuePtr=InProperty->FieldProperty->GetValuePointer(InFieldPtr);
          return SerializeToJson(ValuePtr,Type,IsBinaryValue);
     }
     else
     {
          assert(InProperty->NonStaticProperty);
          auto Value =InProperty->NonStaticProperty->GetValue(InFieldPtr);
          return SerializeToJson(Value.GetPointer(),Type,IsBinaryValue);
     }
}

std::shared_ptr<CJsonNodeBase> CJsonSerializer::SerializeToJson(const void *InValuePtr, CType *InType, bool bIsBinaryValue)
{
     auto Type=InType;

     if(Type->IsBasicType())
     {
          if(Type==CGetType<bool>()())
          {
               return std::make_shared< CJsonBoolNode >(
                    *reinterpret_cast<const bool*>(InValuePtr)
               );
          }
          else if(Type==CGetType<int8_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const int8_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<uint8_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const uint8_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<int16_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const int16_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<uint16_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const uint16_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<int32_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const int32_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<uint32_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const uint32_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<int64_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const int64_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<uint64_t>()())
          {
               return std::make_shared< CJsonIntNode >(
                    *reinterpret_cast<const uint64_t*>(InValuePtr)
               );
          }
          else if(Type==CGetType<float>()())
          {
               return std::make_shared< CJsonFloatNode >(
                    *reinterpret_cast<const float*>(InValuePtr)
               );
          }
          else if(Type==CGetType<double>()())
          {
               return std::make_shared< CJsonFloatNode >(
                    *reinterpret_cast<const double*>(InValuePtr)
               );
          }else if(Type==CGetType<CString>()())
          {
               return std::make_shared< CJsonStringNode >(
                    *reinterpret_cast<const CString*>(InValuePtr)
               );
          }
          else if(Type==CGetType<char32_t>()())
          {
               return std::make_shared< CJsonStringNode >(
                    *reinterpret_cast<const char32_t*>(InValuePtr)
               );
          }
          else
          {
               assert(false && "unimplement situation");
          }

     }
     else if(WH::IsStringSerializeable(Type))
     {
          return std::make_shared< CJsonStringNode >(
               WH::SerializeInstanceToString(Type,InValuePtr)
          );
     }
     else if(Type->IsVectorType())
     {   
          CTVectorGeneralized * VectorType= reinterpret_cast<CTVectorGeneralized*>(Type);
          
          const CReflectableVector* VectorBase=reinterpret_cast<const CReflectableVector*>(InValuePtr);

          if(bIsBinaryValue && BinarySerializer)
          {
               //serialize whole vector as binary
               auto DataAndSize=VectorBase->GetDataAndSize(VectorType->GetElementType().BasicType);
               
               //uint64_t FisrtByte=BinarySerializer->GetSize();
               BinarySerializer->Append(DataAndSize.Data,DataAndSize.Size);
               
               //create json node to store binary data size
               return std::make_shared< CJsonIntNode >(DataAndSize.Size);
               
          }
          else
          {
               //serialize whole vector as json array
               std::shared_ptr<CJsonVectorNode> Root= std::make_shared<CJsonVectorNode>();

               for(uint64_t Index=0;Index<VectorBase->Num();Index++)
               {
                    Root->Add(SerializeToJson(
                              VectorBase->At(VectorType->GetElementType().BasicType
                              ,Index
                              ) 
                         ,VectorType->GetElementType().BasicType
                         ,bIsBinaryValue
                         )
                    );
               }
               return Root;
          }

     }
     else if(Type->IsMapType())
     {
          CMapGeneralized * MapType= reinterpret_cast<CMapGeneralized*>(Type);
          const CReflectableMap* MapBase=reinterpret_cast<const CReflectableMap*>(InValuePtr);   

          auto Keys=MapBase->NativeGetKeys(MapType->GetForwardForwardLinkedListType());
          auto Values=MapBase->NativeGetValues(MapType->GetForwardForwardLinkedListType());

          std::shared_ptr<CJsonVectorNode> Root= std::make_shared<CJsonVectorNode>();

          for(uint64_t Index=0;Index<Keys.Num();Index++)
          {
               std::shared_ptr<CJsonObjectNode> Pair= std::make_shared<CJsonObjectNode>();
               Pair->Add(U"Key" ,SerializeToJson(Keys[Index] ,MapType->GetKeyType().BasicType,bIsBinaryValue));
               Pair->Add(U"Value" ,SerializeToJson(Values[Index],MapType->GetValueType().BasicType,bIsBinaryValue));
               Root->Add(Pair);
          }

          return Root;

     }
     else if(Type->IsDelegateType())
     {
          CScriptDelegateGenerialized* DelegateType= reinterpret_cast<CScriptDelegateGenerialized*>(Type);
          const SScriptDelegateBase* Delegate=reinterpret_cast<const SScriptDelegateBase*>(InValuePtr);
          std::shared_ptr<CJsonVectorNode> Root= std::make_shared<CJsonVectorNode>();

          for(int64_t BindingIndex= 0;BindingIndex<Delegate->Bindings.Num();BindingIndex++)
          {
               auto& Binding=Delegate->Bindings[BindingIndex];
               if(Binding.Function->GetType()!=IScriptFunction::EType::ReflectedFunction)
               {
                    //not reflected function, just skip
                    WH::PrintLine(U"Cant serialize Non-reflectable function in delegate, just skip");
                    continue;
               }

               auto ReflectedFunction=std::static_pointer_cast<CReflectedFunctionForScriptDelegate>(Binding.Function);
               auto Object=ReflectedFunction->Object;
               if(!Object)
               {
                    //this binding is invalid, just skip
                    continue;
               }
               CString ObjectString= ObjectSerializer->SerializeToString(Object.Get());
               std::shared_ptr<CJsonObjectNode> BindingJson= std::make_shared<CJsonObjectNode>();
               BindingJson->Add(U"Object",std::make_shared< CJsonStringNode >(ObjectString));
               BindingJson->Add(U"FunctionName",std::make_shared<CJsonStringNode>(ReflectedFunction->Function->Name));

               Root->Add(BindingJson);
          }
          return Root;
     }

     else if(Type->IsSmartObjectPtr())
     {

          RObject* Object=nullptr;
          {
               if(Type->IsWeakObjectPtrType())
               {
                    const RObjectWeakPtr* ObjectPtrPtr=reinterpret_cast<const RObjectWeakPtr*>(InValuePtr);
                    if(ObjectPtrPtr->IsValid())
                    {
                         Object=ObjectPtrPtr->Get();
                    }
               }
               else
               {
                    const RObjectPtr* ObjectPtrPtr=reinterpret_cast<const RObjectPtr*>(InValuePtr);
                    Object=ObjectPtrPtr->Get();
               }
          }

          if(!Object)
          {
               return std::make_shared< CJsonNullNode >();
          }
          CString ObjectString= ObjectSerializer->SerializeToString(Object);
          auto Ret=std::make_shared< CJsonStringNode >(ObjectString);
          return Ret;  
     }
     else if(Type->IsSubclassOfType())
     {
          //CTSubclassOfTypeBase* SubclassType= reinterpret_cast<CTSubclassOfTypeBase*>(Type);
          const SSubclassOfBase* Subclass=reinterpret_cast<const SSubclassOfBase*>(InValuePtr);

          CString SerializedString= Subclass->Get()->GetFullName();

          auto Ret=std::make_shared< CJsonStringNode >(SerializedString);

          return Ret;
     }

     else if(Type->IsClassType())
     {
          assert(false&& "donot support raw pointer now");
     }
     else if(Type->IsStructType())
     {
          std::shared_ptr<CJsonObjectNode> Root= std::make_shared<CJsonObjectNode>();

          for(auto& Property : Type->Properties->GetProperties())
          {
               if(Property->HasFlag(EPropertyFlags::P_NoSerialize))
               {
                    continue;
               }
               Root->Add(Property->GetName(),SerializeToJson(InValuePtr,Property));
          }

          return Root;
     }


     assert(false && "unimplement situation");
     return nullptr;    

}

std::shared_ptr<CJsonNodeBase> CJsonSerializer::SerializeToJson(RObjectPtr InObject)
{
     std::shared_ptr<CJsonObjectNode> Root= std::make_shared<CJsonObjectNode>();
     
     for(auto Property: InObject->GetClass()->Properties->GetProperties())
     {
          if(Property->HasFlag(EPropertyFlags::P_NoSerialize))
          {
               continue;
          }

          Root->Add(Property->GetName(),SerializeToJson(InObject.Get(),Property));
          
     }
     return Root;
}

void CJsonDeserializer::DeserializeFromJson(RObjectPtr InObject, std::shared_ptr<CJsonNodeBase> InJson)
{
     std::shared_ptr<CJsonObjectNode> Root= std::static_pointer_cast<CJsonObjectNode>(InJson);
     
     for(auto Property: InObject->GetClass()->Properties->GetProperties())
     {
          if(Property->HasFlag(EPropertyFlags::P_NoSerialize))
          {
               continue;
          }

          auto Json=Root->Get(Property->GetName());
          DeserializeFromJson(InObject.Get(),Property,Json);
     }
}

void CJsonDeserializer::DeserializeFromJson(void *InFieldPtr, CProperty *InProperty, std::shared_ptr<CJsonNodeBase> InJson)
{
     auto Type=InProperty->GetType().BasicType;
     
     bool IsBinaryValue=InProperty->HasFlag(EPropertyFlags::P_SerializeAsBinary);

     if(InProperty->IsField())
     {
          //auto MemberVariableProperty=static_cast<CFieldProperty*>(InProperty);
          auto ValuePtr=InProperty->FieldProperty ->GetValuePointer(InFieldPtr);
          DeserializeFromJson(ValuePtr,Type,InJson,IsBinaryValue);
     }
     else
     {
          assert(InProperty->NonStaticProperty);
          auto Value =InProperty->NonStaticProperty->GetValue(InFieldPtr);
          
          DeserializeFromJson(Value.GetPointer(),Type,InJson,IsBinaryValue);
          InProperty->NonStaticProperty->SetValue(InFieldPtr,Value);
     }


}

void CJsonDeserializer::DeserializeFromJson(void *InValuePtr, CType *InType, std::shared_ptr<CJsonNodeBase> InJson,bool bIsBinaryValue)
{
     auto Type=InType;
     
     if(Type->IsBasicType())
     {
          if(Type==CGetType<bool>()())
          {
               *reinterpret_cast<bool*>(InValuePtr)=std::static_pointer_cast<CJsonBoolNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<int8_t>()())
          {
               *reinterpret_cast<int8_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<uint8_t>()())
          {
               *reinterpret_cast<uint8_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<int16_t>()())
          {
               *reinterpret_cast<int16_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<uint16_t>()())
          {
               *reinterpret_cast<uint16_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<int32_t>()())
          {
               *reinterpret_cast<int32_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<uint32_t>()())
          {
               *reinterpret_cast<uint32_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<int64_t>()())
          {
               *reinterpret_cast<int64_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<uint64_t>()())
          {
               *reinterpret_cast<uint64_t*>(InValuePtr)=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<float>()())
          {
               *reinterpret_cast<float*>(InValuePtr)=std::static_pointer_cast<CJsonFloatNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<double>()())
          {
               *reinterpret_cast<double*>(InValuePtr)=std::static_pointer_cast<CJsonFloatNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<CString>()())
          {
               *reinterpret_cast<CString*>(InValuePtr)=std::static_pointer_cast<CJsonStringNode>(InJson)->GetValue();
          }
          else if(Type==CGetType<char32_t>()())
          {
               *reinterpret_cast<char32_t*>(InValuePtr)=std::static_pointer_cast<CJsonStringNode>(InJson)->GetValue().GetData()[0];
          }
          else
          {
               assert(false && "unimplement situation");
          }
     }
     else if(WH::IsStringSerializeable(Type))
     {
          CString SerializedString=std::static_pointer_cast<CJsonStringNode>(InJson)->GetValue();
          WH::DeserializeInstanceFromString(SerializedString,Type,InValuePtr);
     }
     else if(Type->IsVectorType())
     {   
          CTVectorGeneralized * VectorType= reinterpret_cast<CTVectorGeneralized*>(Type);

          CReflectableVector* VectorBase=reinterpret_cast<CReflectableVector*>(InValuePtr);

          if(bIsBinaryValue && BinaryDeserializer)
          {
               //deserialize whole vector as binary
               uint64_t DataSize=std::static_pointer_cast<CJsonIntNode>(InJson)->GetValue();
               void* Data=BinaryDeserializer->TakeOut(DataSize);
               VectorBase->SetDataAndSize(VectorType->GetElementType().BasicType,Data,DataSize);
          }
          else
          {
               VectorBase->Clear(VectorType->GetElementType().BasicType);

               auto Json=std::static_pointer_cast<CJsonVectorNode>(InJson);
               VectorBase->Resize(VectorType->GetElementType().BasicType,Json->Num());

               for(uint64_t Index=0;Index<VectorBase->Num();Index++)
               {
                    DeserializeFromJson(VectorBase->At(VectorType->GetElementType().BasicType,Index),VectorType->GetElementType().BasicType,Json->Get(Index),bIsBinaryValue);
               }
          }
     }
     else if(Type->IsMapType())
     {
          CMapGeneralized * MapType= reinterpret_cast<CMapGeneralized*>(Type);
          CReflectableMap* MapBase=reinterpret_cast<CReflectableMap*>(InValuePtr);   
          MapBase->Clear(MapType->GetForwardForwardLinkedListType());

          auto Json=std::static_pointer_cast<CJsonVectorNode>(InJson);
          
          for(auto& PairJson :Json->GetNodes())
          {
               void* KeyData= malloc(MapType->GetKeyType().BasicType->GetSize());
               MapType->GetKeyType().BasicType->InitInstance(KeyData);

               void* ValueData= malloc(MapType->GetValueType().BasicType->GetSize());
               MapType->GetValueType().BasicType->InitInstance(ValueData);

               auto PairDictJson=std::static_pointer_cast<CJsonObjectNode>(PairJson);
               
               auto KeyJson=PairDictJson->Get(U"Key");
               DeserializeFromJson(KeyData,MapType->GetKeyType().BasicType ,KeyJson,bIsBinaryValue);

               auto ValueJson=PairDictJson->Get(U"Value");
               DeserializeFromJson(ValueData,MapType->GetValueType().BasicType ,ValueJson,bIsBinaryValue);

               MapBase->Add(MapType->GetForwardForwardLinkedListType(),KeyData,ValueData);
          }
          
     }
     else if(Type->IsDelegateType())
     {
          CScriptDelegateGenerialized* DelegateType= reinterpret_cast<CScriptDelegateGenerialized*>(Type);
          SScriptDelegateBase* Delegate=reinterpret_cast<SScriptDelegateBase*>(InValuePtr);
          Delegate->Bindings.Clear();

          auto Json=std::static_pointer_cast<CJsonVectorNode>(InJson);

          for(auto& BindingJson :Json->GetNodes())
          {
               auto BindingDictJson=std::static_pointer_cast<CJsonObjectNode>(BindingJson);
               auto ObjectString=std::static_pointer_cast<CJsonStringNode>(BindingDictJson->Get(U"Object"))->GetValue();
               auto FunctionName=std::static_pointer_cast<CJsonStringNode>(BindingDictJson->Get(U"FunctionName"))->GetValue();

               auto Object=ObjectSerializer->DeserializeFromString(ObjectString);
               if(!Object)
               {
                    //this binding is invalid, just skip
                    continue;
               }

               auto Function=Object->GetClass()->FindFunction(FunctionName);
               if(!Function)
               {
                    //this binding is invalid, just skip
                    continue;
               }

               Delegate->Bind(Object,Function);
          }
     }
     else if(Type->IsSmartObjectPtr())
     {
          if(InJson->IsNull())
          {
               if(Type->IsWeakObjectPtrType())
               {
                    RObjectWeakPtr* ObjectPtrPtr=reinterpret_cast<RObjectWeakPtr*>(InValuePtr);
                    *ObjectPtrPtr =nullptr;
               }
               else
               {
                    RObjectPtr* ObjectPtrPtr=reinterpret_cast<RObjectPtr*>(InValuePtr);
                    *ObjectPtrPtr =nullptr;
               }
               return;
          }

          CString ObjectString=std::static_pointer_cast<CJsonStringNode>(InJson)->GetValue();
          RObjectPtr Object=ObjectSerializer->DeserializeFromString(ObjectString);
          
          if(Object)
          {
               if(Type->IsWeakObjectPtrType())
               {
                    RObjectWeakPtr* ObjectPtrPtr=reinterpret_cast<RObjectWeakPtr*>(InValuePtr);
                    *ObjectPtrPtr =Object;
               }
               else
               {
                    RObjectPtr* ObjectPtrPtr=reinterpret_cast<RObjectPtr*>(InValuePtr);
                    *ObjectPtrPtr =Object;
               }
          }          


     }
     else if(Type->IsSubclassOfType())
     {
          CString SerializedString=std::static_pointer_cast<CJsonStringNode>(InJson)->GetValue();

          //find class by name
          auto InnerType=CType::FindType(SerializedString);
          assert(InnerType && "canot find class by name");
          assert(InnerType->IsClassType() && "inner type must be class type");
          reinterpret_cast<SSubclassOfBase*>(InValuePtr)->SetClass(reinterpret_cast<CClass*>(InnerType));
     }
     
     else if(Type->IsClassType())
     {
         
          assert(false&& "donot support raw pointer now");
     }
     else if(Type->IsStructType())
     {
          auto StructJson=std::static_pointer_cast<CJsonObjectNode>(InJson);
          
          for(auto Property : Type->Properties->GetProperties())
          {
               if(Property->HasFlag(EPropertyFlags::P_NoSerialize))
               {
                    continue;
               }
               DeserializeFromJson(InValuePtr,Property,StructJson->Get(Property->GetName()));
          }
     
     }

     

};